﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Pavilion.DesignerTool
{
    public class UndoRedoCollection<T> : UndoRedoController
    {
        private NotifiableCollection<T> Collection { get; set; }
        private Action<int, int> movedItemDelegate;
        private Action<T, int> addedItemDelegate, removedItemDelegate;

        public UndoRedoCollection(NotifiableCollection<T> collection) : base()
        {
            this.Collection = collection;

            movedItemDelegate = (int oldIndex, int newIndex) => { base.AddItem(new UndoRedoItemMoved<T>(this.Collection, oldIndex, newIndex)); };
            removedItemDelegate = (T removedItem, int index) => { base.AddItem(new UndoRedoItemRemoved<T>(this.Collection, removedItem, index)); };
            addedItemDelegate = (T addedItem, int index) => { base.AddItem(new UndoRedoItemAdded<T>(this.Collection, addedItem, index)); };

            RegisterCollectionEvents();
        }

        public override void Undo()
        {
            UnregisterCollectionEvents();
            base.Undo();
            RegisterCollectionEvents();
        }

        public override void Redo()
        {
            UnregisterCollectionEvents();
            base.Redo();
            RegisterCollectionEvents();
        }

        private void RegisterCollectionEvents()
        {
            this.Collection.Moved += movedItemDelegate;
            this.Collection.Removed += removedItemDelegate;
            this.Collection.Added += addedItemDelegate;
        }

        private void UnregisterCollectionEvents()
        {
            this.Collection.Moved -= movedItemDelegate;
            this.Collection.Removed -= removedItemDelegate;
            this.Collection.Added -= addedItemDelegate;
        }
    }

    public class UndoRedoItemMoved<T> : IUndoRedoItem
    {
        private NotifiableCollection<T> Collection { get; set; }
        private int OldIndex { get; set; }
        private int NewIndex { get; set; }

        public UndoRedoItemMoved(NotifiableCollection<T> collection, int oldIndex, int newIndex)
        {
            this.Collection = collection;
            this.OldIndex = oldIndex;
            this.NewIndex = newIndex;
        }

        public void Undo() { this.Collection.Move(NewIndex, OldIndex); }
        public void Redo() { this.Collection.Move(OldIndex, NewIndex); }
    }

    public class UndoRedoItemRemoved<T> : IUndoRedoItem
    {
        private NotifiableCollection<T> Collection { get; set; }
        private T RemovedItem { get; set; }
        private int Index { get; set; }

        public UndoRedoItemRemoved(NotifiableCollection<T> collection, T removedItem, int index)
        {
            this.Collection = collection;
            this.RemovedItem = removedItem;
            this.Index = index;
        }

        public void Undo() { this.Collection.Insert(Index, RemovedItem); }
        public void Redo() { this.Collection.RemoveAt(Index); }
    }

    public class UndoRedoItemAdded<T> : IUndoRedoItem
    {
        private NotifiableCollection<T> Collection { get; set; }
        private T AddedItem { get; set; }
        private int Index { get; set; }

        public UndoRedoItemAdded(NotifiableCollection<T> collection, T addedItem, int index)
        {
            this.Collection = collection;
            this.AddedItem = addedItem;
            this.Index = index;
        }

        public void Undo() { this.Collection.RemoveAt(Index); }
        public void Redo() { this.Collection.Insert(Index, AddedItem); }
    }
}
